#pragma once

#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>


namespace BlockQueueModule
{
    static const int gcap = 10;
    template<class T>
    class BlockQueue
    {
    private:
        bool IsFull()
        {
            return _q.size() == _cap;
        }
        bool IsEmpty()
        {
            return _q.empty();
        }
    public:
        BlockQueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
        {
            pthread_mutex_init(&_mutex,nullptr);
            pthread_cond_init(&_productor_cond,nullptr);
            pthread_cond_init(&_consumer_cond,nullptr);
        }
        void Equeue(const T& in)
        {
            pthread_mutex_lock(&_mutex);
            //想放数据也是要有条件的
            //1.在临界区中等待是必然的!
            while(IsFull())
            {
                std::cout << "生产者进入等待..." << std::endl;
                //2.等待的时候,会释放_mutex
                _pwait_num++;
                pthread_cond_wait(&_productor_cond,&_mutex);//wait的时候,必定是持有锁的
                _pwait_num--;
                //3.返回,线程被唤醒 && 重新申请并持有锁(它会在临界区内醒来)
                std::cout << "生产者被唤醒..." << std::endl;
            }
            //4.到这里,我们一定可以进行生产了
            _q.push(in);

            //一定有数据
            if(_cwait_num)
            {
                pthread_cond_signal(&_consumer_cond);
            }
            pthread_mutex_unlock(&_mutex);
        }
        void Pop(T* out)
        {
            pthread_mutex_lock(&_mutex);
            while(IsEmpty())
            {
                std::cout << "消费者进入等待..." << std::endl;
                _cwait_num++;
                pthread_cond_wait(&_consumer_cond,&_mutex);//伪唤醒
                _cwait_num--;
                std::cout << "消费者被唤醒..." << std::endl;
            }
            //到这里我们必定有数据
            *out = _q.front();
            _q.pop();

            //一定有空间
            if(_pwait_num)
            {
                pthread_cond_signal(&_productor_cond);
            }
            pthread_mutex_unlock(&_mutex);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productor_cond);
            pthread_cond_destroy(&_consumer_cond);
        }
    private:
        std::queue<T> _q;              //保存数据的容器
        int _cap;                      //bq最大容量
        pthread_mutex_t _mutex;        //互斥
        pthread_cond_t _productor_cond;//生产者的条件变量
        pthread_cond_t _consumer_cond; //消费者的条件变量

        int _cwait_num;
        int _pwait_num;
    };
}
